//
// Created by Crestimes on 2024/11/18.
//

#include "ReManager.h"

#include "global.h"

ReManager::ReManager(const vector<string>& buffs) {
    this->buffs = buffs;
    initReMap();
}

/**
 * 初始化reMap
 */
void ReManager::initReMap() {
    for (const auto& line : buffs) {
        // 切割字符串
        size_t split = line.find('=');
        string reName = line.substr(0, split);
        string reValue = line.substr(split + 1, line.length());
        // 将正则值格式化，如[0-9]这种，转成0|1|2|3...|9，并添加连接符号&
        reValue = formatRe(reValue);
        if (reName[0] == '_') {
            // 是最后的某种类型
            reOfTokenType.emplace(reName);
        }
        else {
            // 存在于reOfTokenType正则值中的正则名
            reForAuxiliary.emplace(reName);
        }
        // 保存映射
        reMap[reName] = reValue;
    }
    // 将全部正则值|起来
    for (const auto& reName : reOfTokenType) {
        // 标记__前缀的是关键字，关键字不加入整体正则（整体正则用于分词）
        // 否则下面这种情况会有问题：internet  在使用整体正则分词时该字符会先进入if的分支（i开头）而不是id的分支（letter开头）
        //                        导致因为第二个字符 n!=f 分词失败
        // 将关键字从整体正则中排除后，因为有id分支的存在，所以internet肯定能够被分出来，在后续的类型判断中，因为重命名了id->zid
        // zid的z的字母顺序比if的i字母顺序靠后，所以会先判断类型为if，符合要求
        if (reName.substr(0, 2) != "__") {
            concatRe += "(" + reMap[reName] + ")|";
        }
    }
    concatRe.pop_back();
    // 保存整体正则
    reOfTokenType.emplace(CONCAT_RE_NAME);
    reMap[CONCAT_RE_NAME] = concatRe;
}

/**
 * 判断一个字符是否是运算符
 * @param ch 字符
 * @return 该字符是否运算符
 */
bool ReManager::isOpr(const char ch) {
    return oprSet.count(ch);
}

/**
 * 在原字符串中寻找目标字符串，但是要求目标字符串前面不能是转义字符
 * @param str 原字符串
 * @param target 目标字符串
 * @param pos 起始位置
 * @return 目标字符串在原字符串的起始位置，不存在则返回-1
 */
int ReManager::findWithoutEscape(const string& str, const char target, const int pos) {
    for (int i = pos; i < str.length(); i++) {
        if (str[i] == target && (i == 0 || str[i - 1] != '\\')) {
            // 要么是0下标，要么前一个不是转义字符
            return i;
        }
    }
    return -1;
}

/**
 * 将正则表达式中的中括号转换为小括号和或运算的形式
 * in:  g[abc-g0-5]ab[1-4]gg
 * out: g(a|b|c|d|e|f|g|0|1|2|3|4|5)ab(1|2|3|4)gg
 * @param reValue 原正则值
 * @return 格式化后的正则值
 */
string ReManager::formatRe(string reValue) {
    int leftPos = findWithoutEscape(reValue, '[');
    while (leftPos != -1) {
        // 有中括号，可能有多个
        const int rightPos = findWithoutEscape(reValue, ']');
        // innerValue不包含[]
        string innerValue = reValue.substr(leftPos + 1, rightPos - leftPos - 1);
        // 找里面的 '-'，可能有多个
        int linePos = findWithoutEscape(innerValue, '-');
        while (linePos != -1) {
            // 找到前后两个字符，如 0-9 ，获取范围内的数字，如 0123456789
            const char st = innerValue[linePos - 1], ed = innerValue[linePos + 1];
            string allValues;
            for (char i = st; i <= ed; i++) {
                allValues += i;
            }
            // 替换
            innerValue.replace(linePos - 1, 3, allValues);
            // 继续寻找
            linePos = findWithoutEscape(innerValue, '-', linePos + 1);
        }
        string transReValue;
        // 替换好 '-' 之后，给全部间隔加上 '|'
        for (int i = 0; i < innerValue.length() - 1; i++) {
            transReValue += innerValue[i];
            transReValue += '|';
        }
        transReValue += innerValue[innerValue.length() - 1];
        // 添加括号防止优先级改变
        transReValue = string("(").append(transReValue).append(")");
        // 替换原来的值
        reValue.replace(leftPos, rightPos - leftPos + 1, transReValue);
        // 继续寻找
        leftPos = findWithoutEscape(reValue, '[', leftPos);
    }
    // 添加&
    string newReValue;
    for (size_t i = 0; i < reValue.length(); i++) {
        const char c = reValue[i];
        newReValue += c;
        if (c == '\\') continue; // 转义符号跳过，不然&会插到转义符号和被转义的字符之间
        // 正则名跳过
        bool isReName = false;
        for (const auto& auxRe : reForAuxiliary) {
            if (i + auxRe.length() - 1 < reValue.length() && reValue.substr(i, auxRe.length()) == auxRe) {
                i += auxRe.length() - 1;
                newReValue += auxRe.substr(1); // 从下标1开始是因为循环刚开始时 newReValue += c; 已经把第一个加上了
                isReName = true;
                break;
            }
        }
        if (isReName) {
            continue;
        }
        if (c != '(' && c != '|') {
            // 除了 (、|、字符
            // 如果后面是字符或(，则中间补&
            if (i < reValue.length() - 1 && reValue[i + 1] != ')' && !isOpr(reValue[i + 1])) {
                newReValue += '&';
            }
        }
    }
    return newReValue;
}
